/*     */ package com.zimbra.cs.milter;
/*     */ 
/*     */ import com.google.common.base.Charsets;
/*     */ import com.google.common.base.Joiner;
/*     */ import com.google.common.base.Strings;
/*     */ import com.google.common.collect.Sets;
/*     */ import com.zimbra.common.account.Key.DistributionListBy;
/*     */ import com.zimbra.common.mime.InternetAddress;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.AccessManager;
/*     */ import com.zimbra.cs.account.Group;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.accesscontrol.Rights.User;
/*     */ import com.zimbra.cs.server.NioConnection;
/*     */ import com.zimbra.cs.server.NioHandler;
/*     */ import com.zimbra.cs.server.NioServer;
/*     */ import com.zimbra.cs.server.ServerConfig;
/*     */ import java.io.IOException;
/*     */ import java.nio.charset.Charset;
/*     */ import java.util.EnumMap;
/*     */ import java.util.HashMap;
/*     */ import java.util.Map;
/*     */ import java.util.Set;
/*     */ import org.apache.mina.core.buffer.IoBuffer;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public final class MilterHandler
/*     */   implements NioHandler
/*     */ {
/*     */   private static final String MACRO_MAIL_ADDR = "{mail_addr}";
/*     */   private static final String MACRO_RCPT_ADDR = "{rcpt_addr}";
/*     */   private static final int SMFIP_NOHELO = 2;
/*     */   private static final int SMFIP_NOMAIL = 4;
/*     */   private static final int SMFIP_NOBODY = 16;
/*     */   private static final int SMFIP_NOHDRS = 32;
/*     */   private static final int SMFIP_NOEOH = 64;
/*     */   private static final int SMFIF_ADDHDRS = 1;
/*     */   private static final int SMFIF_CHGHDRS = 16;
/*     */   private static final byte SMFIR_ACCEPT = 97;
/*     */   private static final byte SMFIR_CONTINUE = 99;
/*     */   private static final byte SMFIR_CHGHEADER = 109;
/*     */   private static final byte SMFIR_TEMPFAIL = 116;
/*     */   private static final byte SMFIR_REPLYCODE = 121;
/*     */   private static final byte SMFIC_OPTNEG = 79;
/*     */   
/*     */   private static enum Context
/*     */   {
/*  63 */     HOSTNAME,  ADDRESS,  PORT,  PROTOFAMILY,  SENDER,  RECIPIENT;
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */     private Context() {}
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 103 */   private static final Charset CHARSET = Charsets.US_ASCII;
/*     */   
/* 105 */   private final Map<Context, String> context = new EnumMap(Context.class);
/* 106 */   private final Set<Group> lists = Sets.newHashSetWithExpectedSize(0);
/*     */   private final Provisioning prov;
/*     */   private final AccessManager accessMgr;
/*     */   private final NioConnection connection;
/*     */   
/*     */   public MilterHandler(NioConnection conn) {
/* 112 */     this.prov = Provisioning.getInstance();
/* 113 */     this.accessMgr = AccessManager.getInstance();
/* 114 */     this.connection = conn;
/*     */   }
/*     */   
/*     */   private void clear() {
/* 118 */     this.context.clear();
/* 119 */     this.lists.clear();
/*     */   }
/*     */   
/*     */   public void connectionClosed() throws IOException
/*     */   {
/* 124 */     ZimbraLog.milter.info("Connection closed");
/* 125 */     dropConnection();
/*     */   }
/*     */   
/*     */   public void connectionIdle() throws IOException
/*     */   {
/* 130 */     ZimbraLog.milter.info("Dropping connection because inactive for more than %s seconds (milter_max_idle_time)", new Object[] { Integer.valueOf(this.connection.getServer().getConfig().getMaxIdleTime()) });
/*     */     
/* 132 */     dropConnection();
/*     */   }
/*     */   
/*     */   public void connectionOpened() throws IOException
/*     */   {
/* 137 */     ZimbraLog.milter.info("Connection opened");
/* 138 */     clear();
/*     */   }
/*     */   
/*     */   public void messageReceived(Object msg) throws IOException
/*     */   {
/* 143 */     MilterPacket command = (MilterPacket)msg;
/*     */     try {
/* 145 */       processCommand(command);
/*     */     } catch (ServiceException e) {
/* 147 */       ZimbraLog.milter.error("Dropping connection due to Server error: %s", e.getMessage(), e);
/* 148 */       dropConnection();
/*     */     }
/*     */   }
/*     */   
/*     */   public void dropConnection()
/*     */   {
/* 154 */     if (this.connection.isOpen()) {
/* 155 */       this.connection.close();
/*     */     }
/*     */   }
/*     */   
/*     */   public void setLoggingContext()
/*     */   {
/* 161 */     ZimbraLog.addConnectionIdToContext(String.valueOf(this.connection.getId()));
/*     */   }
/*     */   
/*     */   public void exceptionCaught(Throwable e)
/*     */   {
/* 166 */     ZimbraLog.milter.info("exception caught - dropping connection", e);
/* 167 */     dropConnection();
/*     */   }
/*     */   
/*     */   private void processCommand(MilterPacket command) throws IOException, ServiceException {
/* 171 */     switch ((char)command.getCommand()) {
/*     */     case 'O': 
/* 173 */       SMFIC_OptNeg();
/* 174 */       break;
/*     */     case 'D': 
/* 176 */       SMFIC_Macro(command);
/* 177 */       break;
/*     */     case 'C': 
/* 179 */       SMFIC_Connect(command);
/* 180 */       break;
/*     */     case 'M': 
/* 182 */       SMFIC_Mail();
/* 183 */       break;
/*     */     case 'R': 
/* 185 */       SMFIC_Rcpt();
/* 186 */       break;
/*     */     case 'L': 
/* 188 */       SMFIC_Header();
/* 189 */       break;
/*     */     case 'E': 
/* 191 */       SMFIC_BodyEOB();
/* 192 */       break;
/*     */     case 'A': 
/* 194 */       SMFIC_Abort();
/* 195 */       break;
/*     */     case 'Q': 
/* 197 */       SMFIC_Quit();
/* 198 */       break;
/*     */     case 'B': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'N': case 'P': default: 
/* 200 */       ZimbraLog.milter.debug("Unimplemended command, sending SMFIR_CONTINUE: %s", new Object[] { command.toString() });
/* 201 */       this.connection.send(new MilterPacket((byte)99));
/*     */     }
/*     */   }
/*     */   
/*     */   private IoBuffer getDataBuffer(MilterPacket command)
/*     */   {
/* 207 */     byte[] data = command.getData();
/* 208 */     if ((data != null) && (data.length > 0)) {
/* 209 */       IoBuffer buf = IoBuffer.allocate(data.length, false);
/* 210 */       buf.put(data);
/* 211 */       buf.flip();
/* 212 */       return buf;
/*     */     }
/* 214 */     return null;
/*     */   }
/*     */   
/*     */   private String normalizeAddr(String a)
/*     */   {
/* 219 */     String addr = a.toLowerCase();
/* 220 */     int lb = addr.indexOf('<');
/* 221 */     int rb = addr.indexOf('>');
/* 222 */     return (lb >= 0) && (rb > lb) ? addr.substring(lb + 1, rb) : addr;
/*     */   }
/*     */   
/*     */   private void getAddrFromMacro(IoBuffer macroData, String macro, Context attr) throws IOException {
/* 226 */     Map<String, String> macros = parseMacros(macroData);
/* 227 */     String addr = (String)macros.get(macro);
/* 228 */     if (addr != null) {
/* 229 */       String value = normalizeAddr(addr);
/* 230 */       this.context.put(attr, value);
/* 231 */       ZimbraLog.milter.debug("For macro '%s' %s=%s", new Object[] { macro, attr, value });
/*     */     }
/*     */   }
/*     */   
/*     */   private Map<String, String> parseMacros(IoBuffer buf) throws IOException {
/* 236 */     Map<String, String> macros = new HashMap();
/* 237 */     while (buf.hasRemaining()) {
/* 238 */       String key = buf.getString(CHARSET.newDecoder());
/* 239 */       if (buf.hasRemaining()) {
/* 240 */         String value = buf.getString(CHARSET.newDecoder());
/* 241 */         if ((key != null) && (value != null)) {
/* 242 */           macros.put(key, value);
/*     */         }
/*     */       }
/*     */     }
/* 246 */     return macros;
/*     */   }
/*     */   
/*     */   private void SMFIR_ReplyCode(String code, String reason) {
/* 250 */     int len = 5 + reason.length() + 1;
/* 251 */     String dataStr = code + " " + reason;
/* 252 */     byte[] data = new byte[len - 1];
/*     */     
/* 254 */     int dataStrLen = dataStr.length();
/* 255 */     for (int i = 0; i < dataStrLen; i++) {
/* 256 */       data[i] = ((byte)dataStr.charAt(i));
/*     */     }
/* 258 */     data[dataStrLen] = 0;
/* 259 */     this.connection.send(new MilterPacket(len, (byte)121, data));
/*     */   }
/*     */   
/*     */   private void SMFIR_ChgHeader(int index, String name, String value) throws IOException {
/* 263 */     ZimbraLog.milter.info("Add %s: %s", new Object[] { name, value });
/*     */     
/* 265 */     IoBuffer buf = IoBuffer.allocate(6 + name.length() + value.length());
/* 266 */     buf.putUnsignedInt(index);
/* 267 */     buf.putString(name, name.length() + 1, CHARSET.newEncoder());
/* 268 */     buf.putString(value, value.length() + 1, CHARSET.newEncoder());
/* 269 */     this.connection.send(new MilterPacket(buf.position() + 1, (byte)109, buf.array()));
/*     */   }
/*     */   
/*     */   private void SMFIC_Connect(MilterPacket command) throws IOException {
/* 273 */     ZimbraLog.milter.debug("SMFIC_Connect");
/* 274 */     IoBuffer data = getDataBuffer(command);
/* 275 */     if (data != null) {
/* 276 */       this.context.put(Context.HOSTNAME, data.getString(CHARSET.newDecoder()));
/* 277 */       this.context.put(Context.PROTOFAMILY, new String(new byte[] { data.get() }, CHARSET));
/* 278 */       this.context.put(Context.PORT, String.valueOf(data.getUnsignedShort()));
/* 279 */       this.context.put(Context.ADDRESS, data.getString(CHARSET.newDecoder()));
/* 280 */       ZimbraLog.milter.info("Connection Info %s", new Object[] { this.context });
/*     */     }
/* 282 */     this.connection.send(new MilterPacket((byte)99));
/*     */   }
/*     */   
/*     */   private void SMFIC_Mail() {
/* 286 */     ZimbraLog.milter.debug("SMFIC_Mail");
/* 287 */     this.connection.send(new MilterPacket((byte)99));
/*     */   }
/*     */   
/*     */   private void SMFIC_Rcpt() throws ServiceException {
/* 291 */     ZimbraLog.milter.debug("SMFIC_Rcpt");
/* 292 */     String sender = (String)this.context.get(Context.SENDER);
/* 293 */     if (sender == null) {
/* 294 */       ZimbraLog.milter.warn("Empty sender");
/*     */     }
/* 296 */     String rcpt = (String)this.context.get(Context.RECIPIENT);
/* 297 */     if (rcpt == null) {
/* 298 */       ZimbraLog.milter.warn("Empty recipient");
/*     */     }
/* 300 */     if ((sender == null) || (rcpt == null)) {
/* 301 */       this.connection.send(new MilterPacket((byte)116));
/* 302 */       return;
/*     */     }
/* 304 */     if (this.prov.isDistributionList(rcpt)) {
/* 305 */       Group group = this.prov.getGroupBasic(Key.DistributionListBy.name, rcpt);
/* 306 */       if (group != null) {
/* 307 */         if (!this.accessMgr.canDo(sender, group, Rights.User.R_sendToDistList, false)) {
/* 308 */           ZimbraLog.milter.debug("Sender is not allowed to email this distribution list: %s", new Object[] { rcpt });
/* 309 */           SMFIR_ReplyCode("571", "571 Sender is not allowed to email this distribution list: " + rcpt);
/* 310 */           return;
/*     */         }
/* 312 */         this.lists.add(group);
/* 313 */         ZimbraLog.milter.debug("group %s has been added into the list.", new Object[] { group });
/*     */       } else {
/* 315 */         ZimbraLog.milter.debug("rcpt %s is a list but not a group?", new Object[] { rcpt });
/*     */       }
/*     */     } else {
/* 318 */       ZimbraLog.milter.debug("%s is not a distribution list.", new Object[] { rcpt });
/*     */     }
/* 320 */     this.connection.send(new MilterPacket((byte)99));
/*     */   }
/*     */   
/*     */   private void SMFIC_Abort() {
/* 324 */     ZimbraLog.milter.info("SMFIC_Abort session reset");
/* 325 */     clear();
/*     */   }
/*     */   
/*     */   private void SMFIC_Macro(MilterPacket command) throws IOException {
/* 329 */     ZimbraLog.milter.debug("SMFIC_Macro");
/* 330 */     IoBuffer data = getDataBuffer(command);
/* 331 */     if (data != null) {
/* 332 */       byte cmd = data.get();
/* 333 */       if ((char)cmd == 'M') {
/* 334 */         getAddrFromMacro(data, "{mail_addr}", Context.SENDER);
/* 335 */       } else if ((char)cmd == 'R') {
/* 336 */         getAddrFromMacro(data, "{rcpt_addr}", Context.RECIPIENT);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void SMFIC_OptNeg() {
/* 342 */     ZimbraLog.milter.debug("SMFIC_OptNeg");
/* 343 */     IoBuffer data = IoBuffer.allocate(12, false);
/* 344 */     data.putInt(2);
/* 345 */     data.putInt(17);
/* 346 */     data.putInt(118);
/* 347 */     byte[] dataArray = new byte[12];
/* 348 */     System.arraycopy(data.array(), 0, dataArray, 0, 12);
/* 349 */     this.connection.send(new MilterPacket(13, (byte)79, dataArray));
/*     */   }
/*     */   
/*     */   private void SMFIC_Header() {
/* 353 */     ZimbraLog.milter.debug("SMFIC_Header");
/* 354 */     this.connection.send(new MilterPacket((byte)97));
/*     */   }
/*     */   
/*     */   private void SMFIC_BodyEOB() throws IOException {
/* 358 */     ZimbraLog.milter.debug("SMFIC_BodyEOB");
/* 359 */     Set<String> listAddrs = Sets.newHashSetWithExpectedSize(this.lists.size());
/* 360 */     Set<String> replyToAddrs = Sets.newHashSetWithExpectedSize(this.lists.size());
/* 361 */     for (Group group : this.lists)
/* 362 */       if (group == null) {
/* 363 */         ZimbraLog.milter.warn("null group in group list!?!");
/*     */       }
/*     */       else {
/* 366 */         listAddrs.add(group.getMail());
/* 367 */         if (group.isPrefReplyToEnabled()) {
/* 368 */           String addr = group.getPrefReplyToAddress();
/* 369 */           if (Strings.isNullOrEmpty(addr)) {
/* 370 */             addr = group.getMail();
/*     */           }
/* 372 */           String disp = group.getPrefReplyToDisplay();
/* 373 */           if (Strings.isNullOrEmpty(disp)) {
/* 374 */             disp = group.getDisplayName();
/*     */           }
/* 376 */           replyToAddrs.add(new InternetAddress(disp, addr).toString());
/*     */         }
/*     */       }
/* 379 */     if (!listAddrs.isEmpty()) {
/* 380 */       SMFIR_ChgHeader(1, "X-Zimbra-DL", Joiner.on(", ").join(listAddrs));
/*     */     }
/* 382 */     if (!replyToAddrs.isEmpty()) {
/* 383 */       SMFIR_ChgHeader(1, "Reply-To", Joiner.on(", ").join(replyToAddrs));
/*     */     }
/* 385 */     this.connection.send(new MilterPacket((byte)97));
/*     */   }
/*     */   
/*     */   private void SMFIC_Quit() {
/* 389 */     ZimbraLog.milter.info("SMFIC_Quit");
/* 390 */     dropConnection();
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/milter/MilterHandler.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */